﻿using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Collections.Generic;

namespace CustomNetworking
{
    class Program
    {
        static void Main(string[] args)
        {

            Task t1 = new Task(() => new HammerTimeClass(4000, 1).run());
            Task t2 = new Task(() => new HammerTimeClass(4001, 2).run());
            t1.Start();
            t2.Start();

            Console.ReadLine();
        }
    }

    public class HammerTimeClass
    {
        // Data that is shared across threads
        private ManualResetEvent mre1;
        private ManualResetEvent mre2;
        private List<string> strings = new List<string>();
        private List<object> payloads = new List<object>();

        private readonly object stringLock = new object();
        private readonly object payloadLock = new object();

        // Timeout used in test case
        //private static int timeout = 10000;

        // The number of sends/receives attempted
        int count;

        int port;
        int version;

        public HammerTimeClass(int p, int v)
        {
            port = p;
            version = v;
        }

        public void run()
        {
            
            
            // Create and start a server.
            TcpListener server = new TcpListener(IPAddress.Any, port);
            server.Start();

            // Create a client.
            TcpClient client = new TcpClient("localhost", port);

            Socket serverSocket = server.AcceptSocket();
            Socket clientSocket = client.Client;

            // Wrap the two ends of the connection into StringSockets
            StringSocket sendSocket = new StringSocket(serverSocket, new UTF8Encoding());
            StringSocket receiveSocket = new StringSocket(clientSocket, new UTF8Encoding());

            mre1 = new ManualResetEvent(false);
            mre2 = new ManualResetEvent(false);

            List<string> expectedStrings = new List<string>();
            List<object> expectedObjects = new List<object>();

            count = 50000;

            // make a bunch of receive requests
            for (int i = 0; i < count; i++)
            {
                receiveSocket.BeginReceive(CompletedReceive, i + 1);
                expectedStrings.Add("Test " + (i + 1));
            }

            // send a bunch of strings
            for (int i = 0; i < count; i++)
            {
                sendSocket.BeginSend("Test " + (i + 1) + "\n", CompletedSend, i + 1);
                expectedObjects.Add(i + 1);
            }

            mre1.WaitOne();
            mre2.WaitOne();

            server.Stop();
            client.Close();

            Console.WriteLine("{0} Strings sent:     {1}", version, payloads.Count);
            Console.WriteLine("{0} Strings received: {1}", version, strings.Count);
        }


        // receive callback
        private void CompletedReceive(String s, Exception o, object payload)
        {
            lock (stringLock)
            {
                strings.Add(s);
            }
            if (strings.Count == count)
            {
                mre2.Set();
            }
        }

        // send callback
        private void CompletedSend(Exception o, object payload)
        {
            lock (payloadLock)
            {
                payloads.Add(payload);
            }

            if (payloads.Count == count)
            {
                mre1.Set();
            }
        }
    }
}
